简介
ESP8266串口WIFI模块,超低成本(只需10RMB左右)的物联网开发板,而且有非常丰富的引脚,超低的功耗。ESP8266内部有一个完整的 32bit MCU 核心,主频支持80Mz和160Mz。这个模块支持 IEEE802.11 b/g/n 协议,完整的 TCP/IP 协议栈,可以为现有的设备添加联网功能。而且除了C语言,还可以使用 Python, JavaScript, Lua脚本语言来为ESP8266写程序,极大降低了学习ESP8266的门槛。
环境
在 Win10 下进行开发,因为更熟悉 Python 所以使用 MicroPython 来进行开发。
硬件
- ESP8266开发板 这里使用基于 ESP8266-12F 的 D1 WiFi UNO R3 开发板。
- 数据线 普通安卓的数据线就可以,D1 开发板集成了 CH340 USB转TTL芯片。
如果是其他系统的话,需要确保 CH340 的驱动有安装。
D1 WiFi UNO R3 开发板:
软件
- esptool 固件烧录工具
- PuTTY 串口连接工具
- esp8266 MicroPython固件 点此下载
烧录固件
esptool 需要Python运行环境的支持,所以需要提前安装 Python 然后配置好环境变量,或者也可以下载其他的固件烧写工具。
1 | pip install esptool |
装好之后从设备管理器中查看端口,如果是linux环境,应该是 /dev/ttyUSB*
,然后先使用 esptool.py
命令擦除固件数据。
1 | esptool.py --port COM4 erase_flash |
esptool.py v2.3.1
Connecting....
Detecting chip type... ESP8266
Chip is ESP8266EX
Features: WiFi
Uploading stub...
Running stub...
Stub running...
Erasing flash (this may take a while)...
Chip erase completed successfully in 9.7s
Hard resetting via RTS pin...
接着就可以将下载好的固件文件烧录到板子里了,固件的bin文件要选择实际的文件。
1 | esptool.py --port COM4 --baud 460800 write_flash --flash_size=detect 0 esp8266.bin |
esptool.py v2.3.1
Connecting....
Detecting chip type... ESP8266
Chip is ESP8266EX
Features: WiFi
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 460800
Changed.
Configuring flash size...
Auto-detected Flash size: 4MB
Flash params set to 0x0040
Compressed 600888 bytes to 392073...
Wrote 600888 bytes (392073 compressed) at 0x00000000 in 8.8 seconds (effective 546.1 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
烧录成功后就可以打开PuTTY,设置好COM口,波特率 115200 然后连接了。
[开头这里乱码正常]#4 ets_task(40100130, 3, 3fff837c, 4)
OSError: [Errno 2] ENOENT
MicroPython v1.9.3-8-g63826ac5c on 2017-11-01; ESP module with ESP8266
Type "help()" for more information.
>>>
使用
MicroPython
通过 MicroPython 的使用一些代码来认识下 ESP8266 的硬件
MicroPython 简单认识
MicroPython 使用Python3的语法,使用 help('modules')
命令可以看到支持的所有模块,
>>> help('modules')
__main__ http_client_ssl sys urandom
_boot http_server time ure
_onewire http_server_ssl uasyncio/__init__ urequests
_webrepl inisetup uasyncio/core urllib/urequest
apa102 json ubinascii uselect
array lwip ucollections usocket
btree machine uctypes ussl
builtins math uerrno ustruct
dht micropython uhashlib utime
ds18x20 neopixel uheapq utimeq
errno network uio uzlib
esp ntptime ujson webrepl
example_pub_button onewire umqtt/robust webrepl_setup
example_sub_led os umqtt/simple websocket
flashbdev port_diag uos websocket_helper
framebuf select upip
gc socket upip_utarfile
http_client ssd1306 upysh
Plus any modules on the filesystem
>>>
其中跟板子硬件相关的模块主要是 esp
和 machine
,其他大多就是与网络有关的模块了,可以看出来还是比较丰富的。
简单的测试了下,对Python3的语法支持还是比较完善的,除了基本的数据类型和语法甚至 lambda表达式、列表解析、装饰器、生成器也都支持。
具体使用查看 MicroPython官方文档
查看资源信息
内存信息可以使用 micropython.mem_info()
获取
>>> import micropython
>>> micropython.mem_info()
stack: 2112 out of 8192
GC: total: 35968, used: 9744, free: 26224
No. of 1-blocks: 48, 2-blocks: 24, max blk sz: 264, max free sz: 1132
8K的栈,35K的堆。。。不过这资源对于它也是够了。
Flash大小可以使用 esp.flash_size()
获取
>>> import esp
>>> str(esp.flash_size()/1024/1024) + ' MB'
'4.0 MB'
4MB的存储空间,除去系统占用的只是存放一些简单的程序脚本也完全够用了。
测试MCU的速度
ESP8266内置的MCU主频支持80MHz和160MHz,那么测试下这样的速度到底有多快呢。
在终端使用 Ctrl + e
进入代码粘贴模式,然后 Ctrl + d
提交或者 Ctrl + c
取消,然后键入以下代码。
1 | import time |
>>> loop_start()
Present freq: 80MHz
Loop 10000 times for 30.567 ms
Present freq: 160MHz
Loop 10000 times for 15.299 ms
>>>
代码提交后调用 loop_start()
方法,可以看到在默认的 80MHz 下和调节到 160MHz 下的空循环10000次需要多长时间,差不多每次循环使用不到16微妙。其中 micropython.native
装饰器能让代码以机器码的速度运行,如果好奇的话可以试试没有这个装饰器的执行速度。
将板子重启后,修改代码去掉那个加速的装饰器,然后重新提交代码执行测试(重启后可能得退出PuTTY后重连才能操作),可以看到速度慢了十几倍。
>>> loop_start()
Present freq: 80MHz
Loop 10000 times for 460.035 ms
Present freq: 160MHz
Loop 10000 times for 230.091 ms
>>>
网络功能
既然它是WiFi开发板,那么最大的特点应该是可以接入WiFi网络,并且通过网络和其他设备交换数据。
连接WiFi
终端执行 help()
函数会显示一个基础的连接WiFi的方法,现在就来连接到一个WiFi试试。这里为了测试方便,用笔记本开了个热点给开发板用。
ESP8266跟连接WiFi相关的库是 network
库,先来看看这个库的使用
1 | import network |
ESP8266如果断线会自动重连,ifconfig()
方法不仅可以查看当前IP,还可以配置静态IP,只需要将 IP/netmask/gw/DNS
作为一个元组或列表传入即可。比如:
1 | wlan.ifconfig(('192.168.137.2','255.255.255.0','192.168.137.1','119.29.29.29')) |
修改后会立即生效,如果在 wlan.connect()
之前就配置好了静态IP,这样就不会再向路由器发起DHCP请求了。
C:\Users\yunfwe\Desktop>ping 192.168.137.2
正在 Ping 192.168.137.2 具有 32 字节的数据:
来自 192.168.137.2 的回复: 字节=32 时间=1ms TTL=255
来自 192.168.137.2 的回复: 字节=32 时间=3ms TTL=255
来自 192.168.137.2 的回复: 字节=32 时间=1ms TTL=255
来自 192.168.137.2 的回复: 字节=32 时间=1ms TTL=255
192.168.137.2 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 1ms,最长 = 3ms,平均 = 1ms
只要连接WiFi成功后,再次重启开发板也会自动连接,不过配置的静态IP就丢失了,需要重新配置。如果不想让开发板在启动的时候自动连接WiFi,可以在使用后执行 wlan.active(False)
。但是开发板还是保存了WiFi的连接信息,想彻底清除这个连接信息可以执行 wlan.connect('','')
。
如果想看到更多的连接细节,可以启用系统的调式功能
1 | import esp |
AP模式
ESP8266除了可以连接WiFi外,还可以作为一个AP来使用
1 | wlan.disconnect() # 先断开先前的连接 |
如果想更改AP模式分配的网段,也可以使用 ap.ifconfig()
来设置。这个时候用手机或者电脑就可以连接到此设备了。
同时,ESP8266还支持 AP+STA 模式,这样就可以充当一个无线中继器了。还有 SmartConfig
技术,可以实现不连接开发板的情况下自动配置开发板连接WiFi,可惜比较遗憾的是,MicroPython 上并没有实现或者找到如何实现这样的功能。。。
WebREPL
ESP8266 还提供了一个在浏览器上远程打开 MicroPython 提示符的功能就是 WebREPL,而且在这个页面上还可以实现上传文件等功能,把自己写好的代码上传到开发板,然后让它开机运行。
打开这个功能非常简单,首先配置这个服务
1 | import webrepl_setup |
执行后会询问你是否开机自启,输入 E 允许开机自启后需要配置一个连接密码来提高安全性,之后问你是否重启,输入 y 重启后可以看到提示符多了一行信息
WebREPL daemon started on ws://0.0.0.0:8266
Started webrepl in normal mode
OSError: [Errno 2] ENOENT
MicroPython v1.9.3-8-g63826ac5c on 2017-11-01; ESP module with ESP8266
Type "help()" for more information.
>>>
这个时候 可以打开官方提供的连接页面:点此打开,输入IP和端口,连接成功后如下
还记得刚才写的测试MCU速度的脚本吗,现在把这个脚本也加入到系统里吧,这样每次开机后都可以使用这个测试的功能了。
把刚才的程序代码写入到一个名叫 mcu.py
的文件,在终端执行如下代码1
2import os
os.listdir('/')
可以看到,一共有两个文件,一个是 boot.py
,一个是 webrepl.cfg.py
,其中 boot.py
会在开机的时候自动运行,webrepl.cfg.py
里面记录的就是Web上连接的时候需要的密码了。现在将 boot.py
下载下来,然后修改这个文件,在最后一行添加 from mcu import loop_start
,然后将 mcu.py
和 boot.py
上传回去,然后重启板子。
>>> os.listdir('/')
['boot.py', 'webrepl_cfg.py', 'mcu.py']
>>> loop_start()
Present freq: 80MHz
Loop 10000 times for 30.56 ms
Present freq: 160MHz
Loop 10000 times for 15.296 ms
>>>
可以看到,loop_start()
函数在启动的时候就会自动导入了,也就是说 mcu.py
被自动运行了,利用 boot.py
这样就可以实现开机的时候自动为WiFi配置静态IP了。
UDP/TCP
能使用UDP/TCP进行通讯,这才是物联网的第一步,MicroPython 上的 socket
模块提供了对网络套接字的支持。下面用几个例子来看看如何使用。
广播自己的IP地址
开发板连接上WiFi了,但是你并不知道板子的IP怎么办,除了登陆路由器来查看它的IP,还可以让它主动告诉你啊。
下面看代码:1
2
3
4
5
6
7
8
9
10
11
12import time
import socket
import network
def broadcast_ip():
wlan = network.WLAN(network.STA_IF)
while not wlan.isconnected():
time.sleep(1)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for i in range(3):
s.sendto(b'Hi! i am ESP8266!', ('255.255.255.255',2001))
time.sleep(1)
将此代码保存为 ip.py
然后修改 boot.py
文件末尾添加:1
2from ip import broadcast_ip
broadcast_ip()
这里选择了向整个局域网内的2001端口发送UDP广播,也可以定点向某台主机发送,但是如果自己的IP也是DHCP获取的,那么可能下一次就又得该代码了。
然后编写接收端的代码,接收端的代码在自己的电脑上运行。
1 | import socket |
然后保存为 find_esp8266.py
并启动脚本,接着重启开发板,几秒钟后可以看到收到开发板发来的消息了。
C:\Users\yunfwe\Desktop>python find_esp8266.py
Start listen on: 0.0.0.0:2001
From: 192.168.137.197:4097 Message: Hi! i am ESP8266!
From: 192.168.137.197:4097 Message: Hi! i am ESP8266!
From: 192.168.137.197:4097 Message: Hi! i am ESP8266!
获取NAT出口的公网IP
局域网内的主机都是通过NAT方式共享一个公网IP来访问互联网的,那么怎么获取自己出口的这个IP呢?如果自己有台公网上的服务器,让ESP8266向它发个包就知道了,可是大多数人都是没有公网上的服务器的,这样还可以利用第三方提供的查询服务。
这里使用搜狐的查询接口: 点此打开,通过原始TCP套接字手动构建一个 HTTP 请求来访问数据
1 | import json |
在板子上执行看看吧!
硬件IO
通过网络来控制硬件,这样才能构建出各种智能硬件,先看看这块板子的引脚说明
引脚 | 说明 | IC 内部引脚 |
---|---|---|
D0(RX) | 串口接收 | GPIO3 |
D1(TX) | 串口发送 | GPIO1 |
D2 | I/O,不支持中断、PWM、I2C、以及1-wire | GPIO16 |
D3/SCL/D15 | I/O,默认模式下I2C的SCL | GPIO5 |
D4/SDA/D14 | I/O,默认模式下I2C的SDA | GPIO4 |
D5/SCK/D13 | I/O,SPI的时钟 | GPIO14 |
D6/MISO/D11 | I/O,SPI的MISO | GPIO12 |
D7/MOSI/D11 | I/O,SPI的MOSI | GPIO13 |
D8 | I/O,上拉,低电平时进入FLASH模式 | GPIO0 |
D9/TX1 | I/O,上拉 | GPIO2 |
D10/SS | I/O,下拉,SPI时默认的片选(SS) | GPIO15 |
A0 | AD输入,0-3.3V | ADC |
- 所有的IO工作电平为 3.3V,可瞬间承受 5V
- 除D2外,所有 I/O 都支持中断,PWM,I2C,和 1-wire
GPIO
ESP8266-12F 板载了一颗蓝光的LED灯,就试试用代码控制这个灯吧。这颗灯会在 GPIO2 输出为高电平的时候灭掉,输出为低电平的时候亮起来,这样通过控制 GPIO2 口的电平高低就可以控制这颗LED的亮灭了。
ESP8266 跟硬件控制相关的库是 machine
,下面看看用 MicroPython 如何控制 GPIO
1 | from machine import Pin |
可以看到,执行 led = Pin(2, Pin.OUT)
的时候,本来灭着的LED灯突然亮了,接着执行 led.value(1)
的时候,LED灯灭了,这个时候 GPIO2 引脚输出的是高电平,如果在这个引脚上接一个LED灯或者其他设备,这个设备就开始工作了。
D1 WiFi UNO R3 这块板子上还板载了一颗接在 GPIO 14号引脚的LED灯,也可以通过 GPIO 直接控制这颗灯的亮灭,利用这个灯做一个 Blink LED 吧。
1 | import time |
这颗LED灯已经开始以亮 0.5 秒,灭 1 秒的频率无限的闪下去了。
PWM
通过设置 GPIO 口的输出电平,只能控制灯的亮灭,那有没有什么办法可以调节灯的亮度呢,那就是 PWM。
那什么是 PWM 呢?还记得刚才让 LED 灯闪烁的例子吗,亮 0.5 秒,灭 1 秒,这样的频率肉眼很容易就观察出来了。那么如果减少这个休眠的时间,亮 1 毫秒,灭 2 毫秒,这样的频率,人的肉眼几乎就观察不出来了,而且看到的就是LED只有原来的 1/3 的亮度了。这里就引出了一个概念:占空比 通电时间占总时间的比例。而 PWM 就是调节这个的。
下面使用PWM测试下 GPIO14 上的这颗LED灯
1 | from machine import Pin, PWM |
利用这个性质可以做出一个好看的呼吸灯特效,下面看代码
1 | from time import sleep_ms |
Timer
ESP8266的内存实在太小了,因此 MicroPython 并没有为ESP8266实现多线程的功能,那么如果想通过网络来控制呼吸灯应该怎么做到呢?可以试试用定时器。
定时器类似于 JavaScript 的 setTimeout
和 setInterval
,因为它们实现的功能是一摸一样的。MicroPython 的 machine.Timer()
会在给定的时间段执行一次或者周期性的执行这个回掉函数,下面看看具体是如何使用的。
1 | from machine import Timer |
参数 period
是每个周期的时间,单位是毫秒。mode
是运行模式,是只运行一次还是周期性运行。callback
则是到了这个时间周期然后执行的函数。如果这个 period
时间非常短的话,而且周期性的运行一个函数,这个函数将自己的数据保存在全局环境中,等到下个运行周期到了再继续处理数据,如果存在多个不同回掉函数的定时器,切换速度非常快的情况下,不就相当于多个函数在同时运行了吗。定时器每次调用回掉函数的时候,还会将自己本身作为参数传给回掉函数。
下面看代码,运用上面所学的网络套接字,呼吸灯,还有定时器的知识来完成一个可以在局域网甚至公网来控制的呼吸灯。
1 | import time |
将代码中的连接 WiFi 的SSID
和PASSWORD
换成实际的,keep_alive
函数主要是保持一条与公网主机的UDP通道,如果没有公网主机可以注释掉下面 keep_alive_timer
定时器的语句。程序启动了一个UDP端口,接受来自局域网主机的控制,如果存在公网主机,也可以在公网主机上向开发板发送数据。UDP套接字设置为了非阻塞模式,然后定时器每10毫秒会询问一次是否收到UDP数据,如果收到了 on
或者 start
数据,就启动呼吸灯的定时器,呼吸灯定时器的回掉函数将当前的状态保存在了全局变量中,执行一次更改亮度的操作后就退出了,等待下个10毫秒继续执行。这样多个定时器如果存在互相调用,就不会因为发生阻塞而影响其他定时器的运行了。
当ESP8266和公网建立通信后,那么可以玩的地方更多了,比如接收温湿度传感器的数据,然后监控环境温度并定期将温度上传到服务器,或者接入微信公众平台用微信来控制单片机,这些就又是一个很大的话题了。
附录
板子还在继续折腾中。。。等折腾出其他好玩的了继续更新